/*
 * Copyright 2003,2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.jakubiak.jpa.translator.proxy;

import eu.jakubiak.jpa.translator.Translator;
import eu.jakubiak.jpa.translator.annotations.Translate;
import eu.jakubiak.jpa.translator.*;
import eu.jakubiak.jpa.translator.annotations.TranslatorFallowMany;
import eu.jakubiak.jpa.translator.annotations.TranslatorFallowOne;
import eu.jakubiak.jpa.translator.provider.Provider;
import eu.jakubiak.jpa.translator.proxy.collections.TranslatorProxyCollection;
import eu.jakubiak.jpa.translator.proxy.collections.TranslatorProxyList;
import eu.jakubiak.jpa.translator.proxy.collections.TranslatorProxyMap;
import eu.jakubiak.jpa.translator.proxy.collections.TranslatorProxySet;
import eu.jakubiak.jpa.translator.serializer.Serializer;
import eu.jakubiak.utils.javabean.BeanProperty;
import eu.jakubiak.utils.javabean.BeanReflector;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Ta klasa powinna być prywatną klasą translatora
 * 
 * 
 * @author jakubiak_a
 */
public class TranslatorProxyMethodInterceptor implements MethodInterceptor {

    private static final Log log = LogFactory.getLog(TranslatorProxyMethodInterceptor.class);
    private Translator translator;
    private Object delegate;
    private BeanReflector reflector;

    public TranslatorProxyMethodInterceptor(Object delegate, Translator translator) {
        this.delegate = delegate;
        this.reflector = new BeanReflector(delegate);
        this.translator = translator;
    }

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (method.getName().startsWith("set")) {
            if (reflector.containsMethod(method.getName())) {
                BeanProperty property = reflector.getProperty(BeanReflector.lowerCaseFirst(method.getName().substring(3)));
                if (property.isAnnotationPresent(Translate.class)) {
                    return set(property, args[0]);
                }
            }
        }
        if (method.getName().startsWith("get")) {
            if (reflector.containsMethod(method.getName())) {
                BeanProperty property = reflector.getProperty(BeanReflector.lowerCaseFirst(method.getName().substring(3)));
                if (property.isAnnotationPresent(Translate.class)) {
                    return get(property);
                }
                if (property.isAnnotationPresent(TranslatorFallowOne.class)) {
                    return translator.translate(get(property));
                }
                if (property.isAnnotationPresent(TranslatorFallowMany.class)) {
                    if (!property.getType().isInterface()) {
                        throw new RuntimeException("TranslatorFallowMany require interface: " + property.getType());
                    } else {
                        Object returnValue = get(property);
                        if (returnValue != null) {
                            if (returnValue instanceof Map) {
                                return new TranslatorProxyMap(translator, (Map) returnValue);
                            } else if (returnValue instanceof Set) {
                                return new TranslatorProxySet(translator, (Set) returnValue);
                            } else if (returnValue instanceof List) {
                                return new TranslatorProxyList(translator, (List) returnValue);
                            } else if (returnValue instanceof Collection) {
                                return new TranslatorProxyCollection(translator, (Collection) returnValue);
                            } else {
                                throw new RuntimeException("Unsupported collection type: " + returnValue.getClass().getName());
                            }
                        }
                    }
                }
            }
        }
        if (method.getName().startsWith("is")) {
            if (reflector.containsMethod(method.getName())) {
                BeanProperty property = reflector.getProperty(BeanReflector.lowerCaseFirst(method.getName().substring(2)));
                if (property.isAnnotationPresent(Translate.class)) {
                    return get(property);
                }
            }
        }
        return method.invoke(delegate, args);
    }

    private Object get(BeanProperty property) throws Throwable {
        return translator.read(property, delegate);
    }

    private Object set(BeanProperty property, Object value) throws Throwable {
        translator.store(property, delegate, value);
        return null;
    }
}
